/**
 * 异步控件，根据关键词查找列表的选择器
 * 场景：
 * 1.账户管理-数据权限管理-新建数据权限(已废弃)
 * 2.机器人流程调试-跳转节点
 * 3.通话记录-状态选择：全选、部分选择
 *
 * @author Elsa
 * @updateDate 2022-3-2
 */
import React, { useState } from 'react';
import { Button, Checkbox, Empty, Input, Pagination, Spin } from 'antd';
import {
  CloseCircleOutlined,
  DownOutlined,
  VerticalLeftOutlined,
  VerticalRightOutlined,
} from '@ant-design/icons';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import './style.scss';

/**
 * PageSelect获取数据异步函数的入参
 */
export interface FetchDataParam {
  keywords: string;
  pageNum: number;
  pageSize: number;
}

export declare type FetchDataResponse = Promise<{
  data: { label: string; value: string | number; data?: any }[];
  total: number;
}>;

export declare type FetchRecordResponse = Promise<{
  label: string;
  value: string | number;
  data?: any;
}>;

interface IProp {
  // 设置select input值
  value?: string | any[];
  // 每页的记录条数
  pageSize?: number;
  // 输入提示语
  placeholder?: string;
  // 是否为多选，默认为false
  multiChoice?: boolean;
  // 是否展示外侧已选项，默认为false
  showOutsideItem?: boolean;
  // 是否在select列每一项增加复选框样式，默认为false
  showCheckBox?: boolean;
  /**pageSelect值变化的回调，控件消费者通过此项来实时获取值;data参数有值，则是回调选中赋值*/
  onChange?: (
    val: string | number | { label: string; value: string | number }[],
    data?: any
  ) => any;
  // PageSelect异步获取数据的接口
  fetchData: (data: FetchDataParam) => FetchDataResponse;
  /** 当pageSelect需要使用setValue时，必须传递此函数来对setValue的入参进行查询 */
  fetchRecord?: (data: string | number) => FetchRecordResponse;
  // 是否允许清空，默认false
  allowClear?: boolean;
  // 是否可以编辑，默认false
  disabled?: boolean;
}

interface IState {
  // 设置select input值
  value?: string | any[];
  // 展示在input的值，并非PageSelect实际暴露的value值，例如：已选2项
  displayValue: string;
  // 加载中
  loading: boolean;
  // 通过异步函数获取的列表
  optionList: { label: string; value: string | number }[];
  // 已选择的项
  selectItem: { label: string; value: string | number }[];
  // 记录总条数
  pageTotal: number;
  // 当前页码
  pageNum: number;
  // 每页条数，默认为10
  pageSize: number;
  // 下拉列表是否显示
  dropListVisible: boolean;
}

/**
 * 此控件是一个携带搜索，翻页功能的select异步控件
 * 结构组成：ant-design-Input
 *          自定义select列表
 *          ant-design-Pagination
 */
export class PageSelect extends React.Component<IProp, IState> {
  /**
   * 同时多次选择项时，由于state中selectitem的存储过程为异步，会导致多次选择最终只被保留一项，记录一个变量在class中，保证需要选择的项不会丢失
   */
  private selectItem: { label: string; value: string | number }[];

  /**
   * 控件设置为多选时，记录每页被选择的项
   */
  private selectItemsMap: Map<number, { label: string; value: string | number }[]>;

  pageSelectRef: React.RefObject<any>;
  constructor(args: IProp) {
    super(args);

    this.selectItem = [];
    this.pageSelectRef = React.createRef();
    this.selectItemsMap = new Map();
    this.state = {
      value: (args.value as string) || '',
      displayValue: (args.value as string) || '',
      loading: false,
      optionList: [],
      selectItem: [],
      pageTotal: 1,
      pageSize: args.pageSize || 10,
      pageNum: 1,
      dropListVisible: false,
    };
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClickOutside, false);
  }
  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside, false);
  }
  // 组件外部被点击-关闭组件
  handleClickOutside = (e: any) => {
    const { target } = e;
    if (this.pageSelectRef?.current && !this.pageSelectRef.current.contains(target)) {
      console.log('pageselect 组件外部被点击===');
      this.closeDropList();
    } else {
      console.log('Pageselect 组件【内部】被点击 ===>>>', this.props?.placeholder || '默认');
    }
  };

  /**
   * 设置已选项值
   */
  public setValue(val: number | string | (number | string)[]): void {
    const { fetchRecord } = this.props;
    if (!fetchRecord) {
      console.warn('use setValue without set fetchRecord!');
      return;
    }
    if (['string', 'number'].includes(typeof val)) {
      // 单选
      val = val as string | number;
      fetchRecord(val)
        .then(res => {
          if (!res) {
            return;
          }
          this.setState(
            {
              optionList: [res],
              pageTotal: 1,
            },
            () => {
              this.handleSelect(res);
            }
          );
        })
        .catch(error => {
          console.error(`系统异常: ${JSON.stringify(error)}`);
        });
    } else {
      // 多选
      // 结果类型为数组类型
      val = val as (number | string)[];
      const promiseGroup: Promise<any>[] = [];

      if (!val) {
        console.error('******pageSelect 回显出问题，值为空', val);
        return;
      }
      val.forEach(arg => {
        promiseGroup.push(fetchRecord(arg));
      });
      Promise.all(promiseGroup)
        .then(res => {
          res = res.filter(item => item && item);
          this.setState(
            {
              optionList: res,
              pageTotal: res.length,
            },
            () => {
              res.forEach(item => {
                this.handleSelect(item);
              });
            }
          );
        })
        .catch(error => {
          console.error(`系统异常: ${JSON.stringify(error)}`);
        });
    }
  }

  /**
   * 全选方法
   */
  public selectAll(checked = true): void {
    this.getListData().then(() => {
      this.doSelectAll({
        target: {
          checked: checked,
        },
      } as any);
    });
  }

  /**
   * 设置已选项值-无翻页的搜索列表
   * 和setValue的区别在于pageSelect的列表永远不会有翻页时，可以相对快捷的选中项
   */
  public setValueWithoutPage(
    val: number | string | (number | string)[],
    findByLabel = false
  ): void {
    this.getListData().then(() => {
      let items: { label: string; value: string | number }[] = [];
      const { optionList } = this.state;
      if (['string', 'number'].includes(typeof val)) {
        items = [
          optionList.find(item => {
            return findByLabel ? item.label === val : item.value === val;
          }) as any,
        ];
      } else {
        (val as (number | string)[]).forEach(value => {
          items.push(
            optionList.find(item => {
              return findByLabel ? item.label === value : item.value === value;
            }) as any
          );
        });
      }
      items.forEach(item => {
        item && this.handleSelect(item);
      });
    });
  }

  /**
   * 获取向外暴露的值
   */
  public getValue(): string | number | { label: string; value: string | number }[] {
    const { multiChoice } = this.props;
    if (this.state.selectItem.length === 0) {
      return multiChoice ? [] : '';
    }
    return multiChoice ? this.state.selectItem : this.state.selectItem[0].value;
  }

  /**
   * 置空控件
   */
  public clear(): void {
    this.selectItem = [];
    this.setState(
      {
        displayValue: '',
        value: [],
        selectItem: [],
        optionList: [],
        pageTotal: 1,
        pageNum: 1,
      },
      () => {
        this.props.onChange && this.props.onChange(this.getValue());
      }
    );
  }

  /**
   * optionList-获取数据
   */
  public getListData(): Promise<void> {
    return new Promise(async resolve => {
      this.setState({
        loading: true,
      });
      const { value, pageNum, pageSize } = this.state;
      const params = {
        keywords: value,
        pageNum: pageNum,
        pageSize: pageSize,
      };
      try {
        const res = await this.props.fetchData(params as FetchDataParam);
        this.setState(
          {
            optionList: res.data || [],
            pageTotal: res.total || 1,
          },
          () => {
            resolve();
          }
        );
      } catch (error) {
        console.error(`系统异常: ${JSON.stringify(error)}`);
      }
      this.setState({
        loading: false,
      });
    });
  }

  /**
   * optionList-展示列表
   */
  private openDropList(): void {
    const { disabled } = this.props;
    if (disabled) return;
    this.setState({ dropListVisible: true });
    this.getListData();
  }

  /**
   * optionList-关闭列表
   */
  private closeDropList(): void {
    const { dropListVisible, selectItem } = this.state;
    if (dropListVisible) {
      // 列表开启时，需要关闭
      if (selectItem.length === 0) {
        // 没有选择项时，关闭需置空控件
        this.clear();
      }
      this.setState({
        dropListVisible: false,
        pageNum: 1,
      });
    }
  }

  /**
   * input值通过输入变化时，将会通过fetchData重新获取数据
   */
  private handleSearch = (e: any): void => {
    const { multiChoice } = this.props;
    const { value } = e.target;
    if (!value) {
      this.clear();
      if (multiChoice) {
        // 全选取消
        this.selectAll(false);
      }
    }

    this.setState(
      {
        value: value,
        displayValue: value,
        pageNum: 1,
      },
      () => {
        this.getListData();
      }
    );
  };

  /**
   * 处理select单项选择/取消选择
   */
  private handleSelect(item: { label: string; value: string | number; data?: any[] }): void {
    const { multiChoice, onChange } = this.props;
    const callBack = () => {
      console.log('pageSelect -回调选中赋值高亮***3', item);
      onChange && onChange(this.getValue(), item.data);
    };
    if (multiChoice) {
      // 已经选择的项
      const selectItem = Array.from(new Set(this.selectItem));
      console.log('查询数组Arr==', this.state.selectItem);
      console.log('查询元素item==', item);
      const index = this.state.selectItem.findIndex(i => i.value === item.value);
      if (index !== -1) {
        // 有该项
        selectItem.splice(index, 1);
      } else {
        // 没有该项
        selectItem.push(item);
      }
      this.selectItem = Array.from(new Set(selectItem));
      this.selectItemsMap.set(this.state.pageNum, this.selectItem);
      this.setState(
        {
          displayValue: this.selectItem.length ? `已选择${this.selectItem.length}项` : '',
          selectItem: this.selectItem,
        },
        () => {
          callBack();
        }
      );
    } else {
      console.log('处理select单项选择/取消选择');
      this.setState(
        {
          displayValue: item.label,
          selectItem: [item],
        },
        () => {
          this.closeDropList();
          callBack();
        }
      );
    }
  }

  /**
   * 全选方法（当前页）
   */
  private doSelectAll(e: CheckboxChangeEvent): void {
    const { checked } = e.target;
    let copySelectItem = this.selectItem.concat([]);
    const { optionList, pageNum } = this.state;

    if (checked) {
      copySelectItem = this.unique(copySelectItem.concat(optionList));
      this.selectItemsMap.set(pageNum, optionList);
    } else {
      this.selectItemsMap.set(pageNum, []);
      copySelectItem = copySelectItem.filter(item => {
        return !!!optionList.find(aim => aim.value === item.value);
      });
    }
    this.setState(
      {
        selectItem: copySelectItem,
      },
      () => {
        this.selectItem = this.state.selectItem;
        this.props.onChange && this.props.onChange(this.getValue());
        this.setState({
          displayValue: this.selectItem.length ? `已选择${this.selectItem.length}项` : '',
        });
      }
    );
  }

  /**
   * 根据key（item.value）对数组去重
   */
  private unique(arr: any[]): any[] {
    const res: any[] = [];
    const len = arr.length;
    for (let i = 0; i < len; i++) {
      if (!res.find(item => item.value === arr[i].value)) {
        res.push(arr[i]);
      }
    }
    return res;
  }

  /**
   * 翻页回调，将会重新获取数据
   */
  private handlePageChange(page?: number, pageSize?: number): void {
    this.setState(
      {
        pageNum: page || this.state.pageNum,
        pageSize: pageSize || this.state.pageSize,
      },
      () => {
        this.getListData();
      }
    );
  }

  /**
   * 删除单项
   */
  private handleDelete(item: { label: string; value: string | number }): void {
    this.selectItem = this.state.selectItem;
    const index = this.selectItem.indexOf(item);
    this.selectItem.splice(index, 1);
    this.setState({
      selectItem: this.selectItem,
      displayValue: this.selectItem.length ? `已选择${this.selectItem.length}项` : '',
    });
    this.props.onChange && this.props.onChange(this.getValue());
  }

  render() {
    const { displayValue, selectItem, optionList, pageTotal, pageSize, pageNum, loading } =
      this.state;
    const {
      placeholder = '请选择',
      disabled,
      allowClear,
      multiChoice,
      showCheckBox,
      showOutsideItem,
    } = this.props;
    const selectAfter = <DownOutlined style={{ fontSize: 12, color: 'rgba(0,0,0,.25)' }} />;
    const item = this.selectItemsMap.get(pageNum) || [];
    const checkAll = item.length === optionList.length;
    const lastPage = parseInt(((pageTotal + pageSize - 1) / pageSize) as any);
    return (
      <div ref={this.pageSelectRef} className="page-select">
        <div
          title={displayValue}
          className="page-select-inputroom"
          onClick={() => {
            this.openDropList();
          }}
        >
          <Input
            onChange={this.handleSearch}
            placeholder={placeholder}
            value={displayValue}
            suffix={selectAfter}
            disabled={disabled}
            allowClear={allowClear}
          />
        </div>
        {this.state.dropListVisible && (
          <div className="page-select-selectroom">
            {/* 多选模式：全选 */}
            {optionList.length > 0 && multiChoice && showCheckBox && (
              <div className="page-select-item all" onClick={() => this.selectAll(!checkAll)}>
                <Checkbox style={{ marginRight: '5px' }} checked={checkAll}></Checkbox>
                <b>全选</b>
              </div>
            )}
            {/* 选项列表-渲染-正在加载 */}
            {loading === true && (
              <div className="page-select-selectroom-null">
                <Spin size="small"></Spin>
              </div>
            )}
            {/* 选项列表-没有数据 */}
            {!loading && optionList.length === 0 && <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
            {/* 选项列表-渲染 */}
            {!loading &&
              optionList.length > 0 &&
              optionList?.map((item, index) => {
                const isChecked = !!selectItem.find(child => child.value === item.value); // 当前项是否被选中
                return (
                  <div
                    title={item.label}
                    key={index}
                    className={
                      isChecked ? 'page-select-item page-select-item-selected' : 'page-select-item'
                    }
                    onClick={() => this.handleSelect(item)}
                  >
                    {showCheckBox && (
                      <Checkbox style={{ marginRight: '5px' }} checked={isChecked}></Checkbox>
                    )}
                    {item.label}
                  </div>
                );
              })}
            {/* 翻页 */}
            {!loading && optionList.length > 0 && (
              <div className="page-select-pagination">
                {lastPage > 1 && (
                  <Button
                    disabled={pageNum === 1 ? true : false}
                    className={
                      pageNum === 1 ? 'ant-pagination-first disabled' : 'ant-pagination-first'
                    }
                    type="link"
                    size="small"
                    icon={<VerticalRightOutlined />}
                    onClick={() => {
                      const first = 1;
                      this.handlePageChange(first);
                    }}
                  />
                )}
                <Pagination
                  simple
                  hideOnSinglePage
                  pageSize={pageSize}
                  defaultCurrent={pageNum}
                  total={pageTotal}
                  onChange={(page, pageSize) => {
                    this.handlePageChange(page, pageSize);
                  }}
                />
                {lastPage > 1 && (
                  <Button
                    disabled={pageNum === lastPage ? true : false}
                    className={
                      pageNum === lastPage ? 'ant-pagination-last disabled' : 'ant-pagination-first'
                    }
                    type="link"
                    size="small"
                    icon={<VerticalLeftOutlined />}
                    onClick={() => {
                      this.handlePageChange(lastPage);
                    }}
                  />
                )}
              </div>
            )}
          </div>
        )}
        {/* 多选：底部展示选项信息 */}
        {multiChoice && showOutsideItem && (
          <div className="page-select-outsideitems">
            {selectItem?.map((item, index) => {
              return (
                <div className="page-select-outsideitem" key={index}>
                  {item.label}
                  <CloseCircleOutlined
                    color="white"
                    onClick={() => this.handleDelete(item)}
                    className="page-select-outsideitem-icon"
                  />
                </div>
              );
            })}
          </div>
        )}
      </div>
    );
  }
}

/**
 * PageSelect测试集
 */
export const PageSelectCollectTest: React.FC = () => {
  const [pageSelectValue1, setPageSelectValue1] = useState<string>('');

  const [pageSelectValue2, setPageSelectValue2] = useState<string>('');

  const [pageSelectValue3, setPageSelectValue3] = useState<string>('');

  const handlePageSelectChange1 = (
    val: string | number | { label: string; value: string | number }[]
  ): void => {
    setPageSelectValue1(typeof val === 'string' ? val : JSON.stringify(val));
  };

  const handlePageSelectChange2 = (
    val: string | number | { label: string; value: string | number }[]
  ): void => {
    setPageSelectValue2(typeof val === 'string' ? val : JSON.stringify(val));
  };

  const handlePageSelectChange3 = (
    val: string | number | { label: string; value: string | number }[]
  ): void => {
    setPageSelectValue3(typeof val === 'string' ? val : JSON.stringify(val));
  };

  const fetchData = (data: FetchDataParam) => {
    const list: any[] = [];
    const keywords = data.keywords;
    const pageNum = data.pageNum;
    const pageSize = data.pageSize;
    const index = (pageNum - 1) * pageSize;
    for (let i = 0; i <= 9; i++) {
      list.push({
        label: keywords + '-list-' + (index + i),
        value: keywords + '-list-' + (index + i),
      });
    }
    return Promise.resolve({
      data: list,
      total: 100,
    });
  };

  const comStyle = { width: '500px', margin: '0 auto' };

  return (
    <div style={comStyle}>
      <h1>pageSelect测试集</h1>
      <h2>1-单选</h2>
      <PageSelect
        placeholder="单选select"
        fetchData={fetchData}
        onChange={handlePageSelectChange1}
        allowClear
      />
      <div>value:{pageSelectValue1}</div>
      <div
        style={{
          width: '500px',
          border: '1px solid black',
          margin: '30px 0 30px 0',
        }}
      ></div>
      <h2>2-多选PageSelect-底部显示已选项</h2>
      <PageSelect
        multiChoice={true}
        showOutsideItem={true}
        placeholder="多选PageSelect-底部显示已选项"
        fetchData={fetchData}
        onChange={handlePageSelectChange2}
        allowClear
      />
      <div>value:{pageSelectValue2}</div>
      <div
        style={{
          width: '500px',
          border: '1px solid black',
          margin: '30px 0 30px 0',
        }}
      ></div>
      <h2>3-多选PageSelect-选项有复选框样式</h2>
      <PageSelect
        multiChoice={true}
        showCheckBox={true}
        placeholder="多选PageSelect-选项有复选框样式"
        fetchData={fetchData}
        onChange={handlePageSelectChange3}
        allowClear
      />
      <div>value:{pageSelectValue3}</div>
      <div
        style={{
          width: '500px',
          border: '1px solid black',
          margin: '30px 0 30px 0',
        }}
      ></div>
    </div>
  );
};
